package com.epic.engine.utils;

import com.alibaba.fastjson.JSON;
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.fluent.Configurations;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.exceptions.JedisConnectionException;
import org.apache.commons.configuration2.PropertiesConfiguration;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class RedisUtils implements Serializable {
    /**
     * 日志接口
     */
    protected static final Logger LOGGER = LoggerFactory.getLogger(RedisUtils.class);
    /**
     * redis连接池
     */
    private static JedisPool jedisPool = null;

    /**
     * 初始化RedisUtils
     */
    public static void init() {
        if (jedisPool == null) {
            synchronized (RedisUtils.class) {
                if (jedisPool == null) {
                    LOGGER.info("init jedisPool");
                    String redisIp = null;
                    Integer redisport = null;
                    Integer timeOut = null;
                    String redisPswd = null;
                    JedisPoolConfig cfg = new JedisPoolConfig();
                    try {
                        String name = "/opt/epic/soc/conf/service/jdbc.properties";
                        PropertiesConfiguration config = new PropertiesConfiguration();
                        Configurations configuration = new Configurations();
                        FileBasedConfigurationBuilder.setDefaultEncoding(PropertiesConfiguration.class,"UTF-8");
                        config =  configuration.properties(RedisUtils.class.getClassLoader().getResource(name));
                        redisIp = config.getString("redis.host");
                        redisport = Integer.valueOf(config.getString("redis.port"));
                        redisPswd = config.getString("redis.password");
                        timeOut = Integer.valueOf(config.getString("redis.timeout"));

                        cfg.setMaxTotal(100);
                        cfg.setMaxIdle(100);
                        cfg.setMinIdle(100);
//                        cfg.setMaxWaitMillis(1000);
                    } catch (Exception e) {
                        LOGGER.error("init redis config failed!", e);
                        return;
                    }
                    if (redisPswd == null || redisPswd.length() == 0) {
                        jedisPool = new JedisPool(cfg, redisIp, redisport, timeOut);
                    } else {
                        jedisPool = new JedisPool(cfg, redisIp, redisport, timeOut, redisPswd);
                    }
                }
            }

        }
    }

    /**
     * 重试策略，每隔2*pastDelay重试一次，最大10次
     * @param jedis
     */
    public static void retryGetJedis(Jedis jedis) {
        long delay = 10;
        for (int i=0; i<10; i++) {
            try {
                Thread.sleep(delay);
                jedis = jedisPool.getResource();
            } catch (InterruptedException e1) {
                LOGGER.info("{}", e1);
            } catch (JedisConnectionException e2) {
                if (i == 9) {
                    throw e2;
                }
                delay*=2;
                continue;
            }
            break;
        }
    }

    /**
     * 从Redis获取数据
     *
     * @param key 值
     * @return value值
     */
    public static String getString(String key) {
        Jedis jedis = null;
        String value = null;
        try {
            if (jedisPool == null) {
                RedisUtils.init();
            }
            try {
                jedis = jedisPool.getResource();
            } catch (JedisConnectionException e) {
                retryGetJedis(jedis);
            }
            value = jedis.get(key);
        } catch (Exception e) {
            LOGGER.error("getJedis exception:", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return value;
    }

    /**
     * 往Redis添加数据
     *
     * @param key   键
     * @param value 值
     * @return value值
     */
    public static String putString(String key, String value) {
        String retValue = null;
        Jedis jedis = null;
        try {
            if (jedisPool == null) {
                RedisUtils.init();
            }
            try {
                jedis = jedisPool.getResource();
            } catch (JedisConnectionException e) {
                retryGetJedis(jedis);
            }
            retValue = jedis.set(key, value);
        } catch (Exception e) {
            LOGGER.error("getJedis exception:", e);
        } finally {
            if (jedis != null) {
                jedis.close();jedisPool.destroy();
            }
        }
        return retValue;
    }


    /**
     * 模糊删除
     *
     * @param keyPattern
     */
    public static void removeFuzzy(String keyPattern) {
        Jedis jedis = null;
        try {
            if (jedisPool == null) {
                RedisUtils.init();
            }
            try {
                jedis = jedisPool.getResource();
            } catch (JedisConnectionException e) {
                retryGetJedis(jedis);
            }
            Set<String> keysSet = jedis.keys(keyPattern + "*");
            if (keysSet != null && keysSet.size() > 0) {
                String[] keys = new String[keysSet.size()];
                keysSet.toArray(keys);
                jedis.del(keys);
            }
        } catch (Exception e) {
            LOGGER.error("getJedis exception:", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    public static void sAdd(String key, String value) {
        Jedis jedis = null;
        try {
            if (jedisPool == null) {
                RedisUtils.init();
            }
            try {
                jedis = jedisPool.getResource();
            } catch (JedisConnectionException e) {
                retryGetJedis(jedis);
            }
            if (value.isEmpty()) {
                value = "";
            }
            jedis.sadd(key, value);

        } catch (Exception e) {
            LOGGER.error("getJedis exception:", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    public static void sAdd(String key, String[] value) {
        Jedis jedis = null;
        try {
            if (jedisPool == null) {
                RedisUtils.init();
            }
            try {
                jedis = jedisPool.getResource();
            } catch (JedisConnectionException e) {
                retryGetJedis(jedis);
            }
            jedis.sadd(key, value);
        } catch (Exception e) {
            LOGGER.error("getJedis exception:", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }


    public static Set<String> sMembers(String key) {
        Jedis jedis = null;
        try {
            if (jedisPool == null) {
                RedisUtils.init();
            }
            try {
                jedis = jedisPool.getResource();
            } catch (JedisConnectionException e) {
                retryGetJedis(jedis);
            }
            return jedis.smembers(key);
        } catch (Exception e) {
            LOGGER.error("getJedis exception:", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return null;
    }

    public static void sRem(String key, String[] values) {
        Jedis jedis = null;
        try {
            if (jedisPool == null) {
                RedisUtils.init();
            }
            try {
                jedis = jedisPool.getResource();
            } catch (JedisConnectionException e) {
                retryGetJedis(jedis);
            }
            jedis.srem(key, values);
        } catch (Exception e) {
            LOGGER.error("getJedis exception:", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * 检查指定的key是否存在
     *
     * @param key
     * @return
     */
    public static Boolean exist(String key) {
        Jedis jedis = null;
        try {
            if (jedisPool == null) {
                RedisUtils.init();
            }
            try {
                jedis = jedisPool.getResource();
            } catch (JedisConnectionException e) {
                retryGetJedis(jedis);
            }
            return jedis.exists(key);
        } catch (Exception e) {
            LOGGER.error("getJedis exception:", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return false;
    }

    public static Boolean sIsMember(String key, Object value) {
        Jedis jedis = null;
        try {
            String objectJson = "";
            if (!(value instanceof String)) {
                objectJson = JSON.toJSONString(value);
            } else {
                objectJson = (String) value;
            }
            if (jedisPool == null) {
                RedisUtils.init();
            }
            try {
                jedis = jedisPool.getResource();
            } catch (JedisConnectionException e) {
                retryGetJedis(jedis);
            }
            return jedis.sismember(key, objectJson);
        } catch (Exception e) {
            LOGGER.error("getJedis exception:", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return false;
    }


    public static void hmset(String key, Map<String, String> value) {
        Jedis jedis = null;
        try {
            if (jedisPool == null) {
                RedisUtils.init();
            }
            try {
                jedis = jedisPool.getResource();
            } catch (JedisConnectionException e) {
                retryGetJedis(jedis);
            }
            if (value.size() > 0) {
                jedis.hmset(key, value);
            }

        } catch (Exception e) {
            LOGGER.error("getJedis exception:", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    public static String hget(String key, String filed) {
        Jedis jedis = null;
        try {
            if (jedisPool == null) {
                RedisUtils.init();
            }
            try {
                jedis = jedisPool.getResource();
            } catch (JedisConnectionException e) {
                retryGetJedis(jedis);
            }
            return jedis.hget(key, filed);
        } catch (Exception e) {
            LOGGER.error("getJedis exception:", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return null;
    }


    public static Map<String, String> hgetall(String key) {
        Jedis jedis = null;
        try {
            if (jedisPool == null) {
                RedisUtils.init();
            }
            try {
                jedis = jedisPool.getResource();
            } catch (JedisConnectionException e) {
                retryGetJedis(jedis);
            }
            return jedis.hgetAll(key);
        } catch (Exception e) {
            LOGGER.error("getJedis exception:", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return null;
    }


    public static void del(String key) {
        Jedis jedis = null;
        try {
            if (jedisPool == null) {
                RedisUtils.init();
            }
            try {
                jedis = jedisPool.getResource();
            } catch (JedisConnectionException e) {
                retryGetJedis(jedis);
            }
            jedis.del(key);
        } catch (Exception e) {
            LOGGER.error("getJedis exception:", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }


    public static void destroy() {
        try {
            if (jedisPool.isClosed()) {
                LOGGER.info("jedisPoll is closed,not need to close again.");
            } else {
                jedisPool.close();
            }
        } catch (Exception e) {
            LOGGER.error("jedisPool close fail.{}", e.getMessage());
        }
    }

    /**
     * 数组类型的缓存更新  1、删除过期数据 2、增加新数据
     *
     * @param key
     * @param newCache
     */
    public static void sUpdate(String key, Set<String> newCache) {
        Set<String> tmp = new HashSet<>();
        Set<String> oldCache = RedisUtils.sMembers(key);
        if (oldCache != null && newCache != null) {
            //清除删除的缓存数据
            tmp.addAll(oldCache);
            tmp.removeAll(newCache);
            if (tmp.size() > 0) {
                String[] tmpArray = new String[tmp.size()];
                tmp.toArray(tmpArray);
                RedisUtils.sRem(key, tmpArray);
            }
            //补充新增的缓存数据
            tmp.clear();
            tmp.addAll(newCache);
            tmp.removeAll(oldCache);
            if (tmp.size() > 0) {
                String[] tmpArray = new String[tmp.size()];
                tmp.toArray(tmpArray);
                RedisUtils.sAdd(key, tmpArray);
            }
        }
    }
}
